home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 (Walnut Creek) / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume89 / devices / ciatimer.1 < prev    next >
Internet Message Format  |  1989-05-11  |  17KB

  1. Path: xanth!ames!oliveb!apple!sun!swap!page
  2. From: page%swap@Sun.COM (Bob Page)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v89i128:  ciatimer - high-precision/accuracy elapsed time v0.1
  5. Message-ID: <104267@sun.Eng.Sun.COM>
  6. Date: 11 May 89 07:55:32 GMT
  7. Sender: news@sun.Eng.Sun.COM
  8. Lines: 550
  9. Approved: page@sun.com
  10.  
  11. Submitted-by: karl@sugar.hackercorp.com (Karl Lehenbauer)
  12. Posting-number: Volume 89, Issue 128
  13. Archive-name: devices/ciatimer01.1
  14.  
  15. This is the second release of my CIA timer stuff, based on original
  16. code by Paul Higginbottom.
  17.  
  18. The two programs herein demonstrate the use of a CIA timer to provide
  19. a high-accuracy, high-resolution time reference for multiple running
  20. programs.  The program 'ciatimer' sets up the timer interrupt routine,
  21. while 'ciafinder' locates the data portion of the interrupt code and
  22. from data therein and the value it reads from the CIA timer register,
  23. determines the elapsed time in seconds and microseconds since the
  24. ciatimer program installed its interrupt.
  25.  
  26. There are more details in the README file, and in the code.  Thanks to
  27. Bob (Kodiak) Burns, Brian P. Dickson and C. Harald Koch for their
  28. fixes to the original; these are described in further detail the
  29. README and code.
  30.  
  31. # This is a shell archive.
  32. # Remove anything above and including the cut line.
  33. # Then run the rest of the file through 'sh'.
  34. # Unpacked files will be owned by you and have default permissions.
  35. #----cut here-----cut here-----cut here-----cut here----#
  36. #!/bin/sh
  37. # shar: SHell ARchive
  38. # Run the following text through 'sh' to create:
  39. #    README
  40. #    ciafinder.c
  41. #    ciatimer.c
  42. #    ciatimer.h
  43. #    makefile
  44. # This is archive 1 of a 1-part kit.
  45. # This archive created: Thu May 11 00:49:48 1989
  46. echo "extracting README"
  47. sed 's/^X//' << \SHAR_EOF > README
  48. XSecond Release of Amiga microsecond-resolution realtime timing routines (0.1)
  49. X-----------------------------------------------------------------------------
  50. X
  51. X    By providing a solid, high-accuracy realtime clock, this code
  52. X    provides a way for timer-releated code that needs to run at
  53. X    specific realtimes, like a SMUS player, MIDI sequencer, etc,
  54. X    to compensate for delays in their execution caused by interrupts,
  55. X    cycle stealing by the blitter, etc.
  56. X
  57. X    What you do is keep track of when in realtime you next want to 
  58. X    run (by adding time intervals to a time returned by ElapsedTime
  59. X    when you start, then when you're ready to set up your timer.device
  60. X    MICROHZ delay timer, call ElapsedTime and calculate the difference 
  61. X    in seconds and microseconds as your arguments for your timer.device
  62. X    request.
  63. X
  64. X    The routine ElapsedTime gets the time by getting the number of
  65. X    55873 microsecond ticks that the handler has seen and retrieving
  66. X    the 0-40000 number of 1.396825 microsecond tisks from the CIA timer
  67. X    registers, scaling them to 1.000 microsecond ticks and returning
  68. X    the shifted-and-or'ed result.
  69. X
  70. X    By using the CIA timer in "continuous" mode, delays in the execution
  71. X    of various routines don't impact the elapsed time timer we maintain.
  72. X
  73. X    Note that what we really want is an improved timer.device where a
  74. X    flag in the timer request could say  "schedule me at this microsecond-
  75. X    resolution time of day seconds and microseconds" instead of only
  76. X    "schedule me in this many seconds and microseconds."
  77. X
  78. X    When the CIA interrupt handler is installed, other tasks need a
  79. X    way to get the count maintained by the timer routine, too.
  80. X
  81. X    This release of the code supports multiple readers:
  82. X
  83. X    There is a sample main routine at the end of this program, run the
  84. X    program in a window to start this guy as a timer.  Control-C within
  85. X    the window to get it to exit.
  86. X
  87. X    Run the ciafinder program from the CLI in another window and it will
  88. X    locate the interrupt data for the timer interrupt maintained by the
  89. X    ciatimer task and give you the elapsed time for your time calculation
  90. X    in this way.
  91. X
  92. X    At the end of the ciafinder program are a couple of routines, commented
  93. X    out, that I use inside my SMUS player to determine the next time I want
  94. X    to run in realtime, using the elapsed timer timer to calculate the
  95. X    delay time for the call to the timer device -- this factors out any
  96. X    delays caused by interrupts, DMA cycle stealing, and other delays in
  97. X    the execution of the routine needing to run at precise intervals.
  98. X
  99. X
  100. XRevisions
  101. X---------
  102. X
  103. XRobert R. Burns (kodiak@amiga.UUCP) pointed out that the hardware manual
  104. Xis incorrect with respect to CIA usage.  Here is the correct usage he
  105. Xprovided:
  106. X
  107. X    CIAA (int 2)
  108. X    timerA      keyboard handshake
  109. X    timerB      uSec timer.device
  110. X    TOD         60Hz timer.device
  111. X    CIAB (int 6)
  112. X    timerA      Commodore serial communication, usually not used
  113. X    timerB      not used
  114. X    TOD         graphics.library beam counter
  115. X
  116. Xciatimer now uses CIAB, timer B
  117. X
  118. XBrian P. Dickson (bpd@dretor.dciem.dnd.ca) identified a problem whereby
  119. Xthe number I was using for the length of a tick, 1.397 microseconds,
  120. Xis actually 1.396825 microseconds.  
  121. X
  122. XAlthough the difference between the two values is less than two nanoseconds,
  123. Xthis error is accumulated every microsecond, causing the timer to run about
  124. X1.7 milliseconds behind per second, or 0.17%, not much, but it's fixable
  125. Xso I adopted his changes which were to change the time ratio from
  126. X46911 timer ticks producing 65536 microseconds to 40000 timer ticks producing
  127. X55873 microseconds.
  128. X
  129. XC. Harald Koch (chk@dretor.dciem.dnd.ca, utzoo!dciem!dretor!chk)
  130. Xreported a bug where a flag used for interrupt control registers
  131. Xwas accidentally used for CIA timer control which happened to
  132. Xwork with timer A and fail with timer B.  The fix is to use the
  133. Xcorrect flag, CIACRAF_START.
  134. X
  135. X
  136. XRegards,
  137. XKarl Lehenbauer @ The Hacker's Haven, Missouri City, Texas
  138. X(Internet: karl@sugar.hackercorp.com, Usenet: uunet!sugar!karl, BIX: kelehen)
  139. SHAR_EOF
  140. echo "extracting ciafinder.c"
  141. sed 's/^X//' << \SHAR_EOF > ciafinder.c
  142. X/* TIMER - Amiga CIA Timer Control Software
  143. X
  144. X  originally by Paul Higginbottom, Public Domain
  145. X
  146. X  hacked on by karl to produce a monotonically increasing microsecond
  147. X  clock, 12/30/88, Public Domain
  148. X
  149. X  second release, 4/27/89
  150. X
  151. X  cc +p ciafinder.c
  152. X  ln ciafinder.o -lcl32
  153. X
  154. X  This code locates the data portion of the timer interrupt installed
  155. X  on CIA B, timer B to provide a high-precision, high-accuracy elapsed
  156. X  time timer.
  157. X
  158. X  It can be used by one of your programs, minus the demo main() routine
  159. X  at the end, to get the elapsed time, provided the companion cia timer
  160. X  interrupt has been installed, typically by the companion program to
  161. X  this one, ciatimer.
  162. X
  163. X*/
  164. X
  165. X#include <exec/types.h>
  166. X#include <exec/tasks.h>
  167. X#include <functions.h>
  168. X#include <exec/interrupts.h>
  169. X#include <hardware/cia.h>
  170. X#include <hardware/custom.h>
  171. X#include <hardware/intbits.h>
  172. X#include <resources/cia.h>
  173. X#include <stdio.h>
  174. X
  175. X#include "ciatimer.h"
  176. X
  177. X#define MATCH 0
  178. X
  179. Xstatic struct Interrupt
  180. X   CIATimerInterrupt,
  181. X   *OldCIAInterrupt = (struct Interrupt *)-1;
  182. X
  183. Xstatic struct Library *CIAResource = NULL;
  184. X
  185. X#define ciatlo ciab.ciatblo
  186. X#define ciathi ciab.ciatbhi
  187. X#define ciacr ciab.ciacrb
  188. X#define CIAINTBIT CIAICRB_TB
  189. X#define CLEAR 0
  190. X
  191. Xvoid DummyCIAInterrupt()
  192. X{
  193. X}
  194. X
  195. X/* install and remove a fake interrupt so we can locate the one we want */
  196. Xstruct CIA_Time *LocateCIATimerData()
  197. X{
  198. X    /* Open the CIA resource */
  199. X    if ((CIAResource = (struct Library *)OpenResource(CIABNAME)) == NULL)
  200. X    {
  201. X        fprintf(stderr,"timer couldn't open cia resource\n");
  202. X        return(NULL);
  203. X    }
  204. X
  205. X    CIATimerInterrupt.is_Node.ln_Type = NT_INTERRUPT;
  206. X    CIATimerInterrupt.is_Node.ln_Pri = 127;
  207. X    CIATimerInterrupt.is_Code = DummyCIAInterrupt;
  208. X    CIATimerInterrupt.is_Node.ln_Name = "ciafinder dummy interrupt";
  209. X
  210. X    /* install interrupt */
  211. X    if ((OldCIAInterrupt = AddICRVector(CIAResource,CIAINTBIT,&CIATimerInterrupt)) == NULL)
  212. X    {
  213. X        RemICRVector(CIAResource, CIAINTBIT, &CIATimerInterrupt);
  214. X        fprintf(stderr,"no CIA timer currently installed!\n");
  215. X        return(NULL);
  216. X    }
  217. X
  218. X    if (strcmp(OldCIAInterrupt->is_Node.ln_Name,CIATIMER_INTERRUPT_NAME) != MATCH)
  219. X    {
  220. X        fprintf(stderr,"CIA interrupt routine is '%s' rather than '%s'\n",OldCIAInterrupt->is_Node.ln_Name,CIATIMER_INTERRUPT_NAME);
  221. X        return(NULL);
  222. X    }
  223. X
  224. X    return((struct CIA_Time *)OldCIAInterrupt->is_Data);
  225. X}
  226. X
  227. X/* return the elapsed real time in seconds and microseconds since the
  228. X * cia timer interrupt handler was installed.
  229. X *
  230. X * ElapsedTime(&secs,µsecs);
  231. X *
  232. X */
  233. Xvoid ElapsedTime(tickdata_ptr,sec_ptr,usec_ptr)
  234. Xstruct CIA_Time *tickdata_ptr;
  235. Xint *sec_ptr,*usec_ptr;
  236. X{
  237. X    register long seconds, microseconds;
  238. X    register long ciahi, cialo;
  239. X
  240. X    Disable();
  241. X    ciahi = ciathi;
  242. X    cialo = ciatlo;
  243. X    seconds = tickdata_ptr->CIA_Seconds;
  244. X    microseconds = tickdata_ptr->CIA_Microseconds;
  245. X    Enable();
  246. X    /* to multiply the timer ticks * 1.397, you can multiply by 1430
  247. X     * and divide by 1024 (or shift right by 10, get it?) 
  248. X     * Note that the timer counts down, so elapsed ticks based
  249. X     * on reading it are computed as CIA_TIME_CLICE minus ticks read
  250. X     */
  251. X    ciahi = CIA_TIME_SLICE - ((ciahi << 8) + cialo);
  252. X    ciahi = ((ciahi * 1430) >> 10) & 0xffff;
  253. X
  254. X    microseconds += ciahi;
  255. X    if (microseconds > 1000000)
  256. X    {
  257. X        microseconds -= 1000000;
  258. X        seconds++;
  259. X    }
  260. X
  261. X    *sec_ptr = seconds;
  262. X    *usec_ptr = microseconds;
  263. X    return;
  264. X}
  265. X
  266. Xmain()
  267. X{
  268. X    struct CIA_Time *CIA_CurrentTime_ptr;
  269. X    long secs, microsecs;
  270. X
  271. X    CIA_CurrentTime_ptr = LocateCIATimerData();
  272. X    if (CIA_CurrentTime_ptr == NULL)
  273. X        exit(1);
  274. X
  275. X    ElapsedTime(CIA_CurrentTime_ptr,&secs,µsecs);
  276. X
  277. X    printf("secs %d microsecs %d\n",secs,microsecs);
  278. X}
  279. X
  280. X
  281. X/*
  282. Xlong reference_seconds, reference_microseconds;
  283. X
  284. Xvoid set_wait_ticks(delay_ticks)
  285. Xlong delay_ticks;
  286. X{
  287. X    register long desired_seconds, desired_microseconds;
  288. X    long current_seconds,current_microseconds;
  289. X
  290. X    timer_request->tr_node.io_Command = TR_ADDREQUEST;
  291. X
  292. X    reference_microseconds += microseconds_per_tick * delay_ticks;
  293. X
  294. X    while (reference_microseconds >= 1000000)
  295. X    {
  296. X        reference_microseconds -= 1000000;
  297. X        reference_seconds++;
  298. X    }
  299. X
  300. X    ElapsedTime(¤t_seconds,¤t_microseconds);
  301. X    desired_seconds = reference_seconds - current_seconds;
  302. X    desired_microseconds = reference_microseconds - current_microseconds;
  303. X
  304. X    if (desired_microseconds < 0)
  305. X    {
  306. X        desired_microseconds += 1000000;
  307. X        desired_seconds--;
  308. X    }
  309. X
  310. X    if (desired_seconds >= 0)
  311. X    {
  312. X        timer_request->tr_time.tv_secs = desired_seconds;
  313. X        timer_request->tr_time.tv_micro = desired_microseconds;
  314. X    }
  315. X    else
  316. X    {
  317. X        timer_request->tr_time.tv_secs = 0;
  318. X        timer_request->tr_time.tv_micro = 1;
  319. X    }
  320. X    SendIO(timer_request);
  321. X}
  322. X
  323. XSetReferenceTime()
  324. X{
  325. X    ElapsedTime(&reference_seconds,&reference_microseconds);
  326. X}
  327. X
  328. X*/
  329. X
  330. SHAR_EOF
  331. echo "extracting ciatimer.c"
  332. sed 's/^X//' << \SHAR_EOF > ciatimer.c
  333. X
  334. X/* TIMER - Amiga CIA Timer Control Software
  335. X
  336. X  originally by Paul Higginbottom, Public Domain
  337. X
  338. X  hacked on by karl to produce a monotonically increasing microsecond
  339. X  clock, 12/30/88, Public Domain
  340. X
  341. X  second version, released 4/27/89, incorporates changes by Bob (Kodiak)
  342. X  Burns, Brian P. Dickson and C. Harald Koch.  Specifically, Bob pointed
  343. X  out that the hardware manual was wrong and CIA B Timer B was the free
  344. X  timer, Brian P. Dickson provided greater accuracy by using 1.396825 for
  345. X  the time constant and determining the time constants based on that
  346. X  number.  Finally, C. Harald Koch found a bug in which the wrong flag
  347. X  was used to initialize a control bit, this made it work for CIA A but
  348. X  not for CIA B.
  349. X
  350. X  cc +p ciatimer.c
  351. X  ln ciatimer.o -lcl32
  352. X
  353. X  To start the timer, execute BeginCIATimer()
  354. X
  355. X  cc +p ciatimer.c
  356. X  ln ciatimer.o -lcl32
  357. X
  358. X    By providing a solid, high-accuracy realtime clock, this code
  359. X    provides a way for timer-releated code that needs to run at
  360. X    specific realtimes, like a SMUS player, MIDI sequencer, etc,
  361. X    to compensate for delays in their execution caused by interrupts,
  362. X    cycle stealing by the blitter, etc.
  363. X
  364. X    What you do is keep track of when in realtime you next want to 
  365. X    run (by adding time intervals to a time returned by ElapsedTime
  366. X    when you start, then when you're ready to set up your timer.device
  367. X    MICROHZ delay timer, call ElapsedTime and calculate the difference 
  368. X    in seconds and microseconds as your arguments for your timer.device
  369. X    request.
  370. X
  371. X    The routine ElapsedTime gets the time by getting the number of
  372. X    55873 microsecond ticks that the handler has seen and retrieving
  373. X    the 0-40000 number of 1.396825 microsecond tisks from the CIA timer
  374. X    registers, scaling them to 1.000 microsecond ticks and returning
  375. X    the shifted-and-or'ed result.
  376. X
  377. X    Note that what we really want is an improved timer.device where a
  378. X    flag in the timer request could say  "schedule me at this microsecond-
  379. X    resolution time of day seconds and microseconds" instead of only
  380. X    "schedule me in this many seconds and microseconds."
  381. X
  382. X    When the CIA interrupt handler is installed, other tasks need a
  383. X    way to get the count maintained by the timer routine, too.
  384. X
  385. X    This release of the code supports multiple readers.
  386. X
  387. X    There is a sample main routine at the end of this program, run the
  388. X    program in a window to start this guy as a timer.  Control-C within
  389. X    the window to get it to exit.
  390. X
  391. X    Run the ciafinder program from the CLI in another window and it will
  392. X    locate the interrupt data for the timer interrupt maintained by the
  393. X    ciatimer task and give you the elapsed time for your time calculation
  394. X    in this way.
  395. X*/
  396. X
  397. X#include <exec/types.h>
  398. X#include <exec/tasks.h>
  399. X#include <functions.h>
  400. X#include <exec/interrupts.h>
  401. X#include <hardware/cia.h>
  402. X#include <hardware/custom.h>
  403. X#include <hardware/intbits.h>
  404. X#include <resources/cia.h>
  405. X#include <stdio.h>
  406. X#include <libraries/dos.h>
  407. X
  408. X#include "ciatimer.h"
  409. X
  410. Xstruct CIA_Time CIA_CurrentTime = {0, 0};
  411. X
  412. Xstatic struct Interrupt
  413. X   CIATimerInterrupt,
  414. X   *OldCIAInterrupt = (struct Interrupt *)-1;
  415. X
  416. Xstatic struct Library *CIAResource = NULL;
  417. X
  418. X#define ciatlo ciab.ciatblo
  419. X#define ciathi ciab.ciatbhi
  420. X#define ciacr ciab.ciacrb
  421. X#define CIAINTBIT CIAICRB_TB
  422. X#define CLEAR 0
  423. X
  424. Xvoid CIAInterrupt()
  425. X{
  426. X    CIA_CurrentTime.CIA_Microseconds += CIA_TIME_QUANTITY;
  427. X    if (CIA_CurrentTime.CIA_Microseconds > 1000000)
  428. X    {
  429. X        CIA_CurrentTime.CIA_Seconds++;
  430. X        CIA_CurrentTime.CIA_Microseconds -= 1000000;
  431. X    }
  432. X}
  433. X
  434. X/* start the timer, clear pending interrupts, and enable timer A
  435. X * Interrupts */
  436. XStartCIATimer()
  437. X{
  438. X    ciacr &= ~(CIACRBF_RUNMODE);    /* set it to reload on overflow */
  439. X    ciacr |= (CIACRBF_LOAD | CIACRBF_START);
  440. X    SetICR(CIAResource,CLEAR|CIAICRF_TB);
  441. X    AbleICR(CIAResource, CIAICRF_SETCLR | CIAICRF_TB);
  442. X}
  443. X
  444. Xvoid StopCIATimer()
  445. X{
  446. X    AbleICR(CIAResource, CLEAR | CIAICRF_TB);
  447. X    ciacr &= ~CIACRBF_START;
  448. X}
  449. X
  450. X/* set period between timer increments */
  451. Xvoid SetCIATimer(micros)
  452. Xunsigned short micros;
  453. X{
  454. X    ciatlo = micros & 0xff;
  455. X    ciathi = micros >> 8;
  456. X}
  457. X
  458. X/* stop the timer and remove its interrupt vector */
  459. XEndCIATimer()
  460. X{
  461. X    if (OldCIAInterrupt == NULL)
  462. X    {
  463. X        StopCIATimer();
  464. X        RemICRVector(CIAResource, CIAINTBIT, &CIATimerInterrupt);
  465. X    }
  466. X}
  467. X
  468. XBOOL BeginCIATimer()
  469. X{
  470. X    /* Open the CIA resource */
  471. X    if ((CIAResource = (struct Library *)OpenResource(CIABNAME)) == NULL)
  472. X    {
  473. X        fprintf(stderr,"cia periodic timer startup couldn't open cia resource\n");
  474. X        return(0);
  475. X    }
  476. X
  477. X    CIATimerInterrupt.is_Node.ln_Type = NT_INTERRUPT;
  478. X    CIATimerInterrupt.is_Node.ln_Pri = 127;
  479. X    CIATimerInterrupt.is_Node.ln_Name =  CIATIMER_INTERRUPT_NAME;
  480. X    CIATimerInterrupt.is_Code = CIAInterrupt;
  481. X    CIATimerInterrupt.is_Data = (APTR)&CIA_CurrentTime;
  482. X
  483. X    /* install interrupt */
  484. X    if ((OldCIAInterrupt = AddICRVector(CIAResource,CIAINTBIT,&CIATimerInterrupt)) != NULL)
  485. X    {
  486. X        fprintf(stderr,"cia timer interrupt already in use by '%s'",OldCIAInterrupt->is_Node.ln_Name);
  487. X        EndCIATimer();
  488. X        return(0);
  489. X    }
  490. X
  491. X    SetCIATimer(CIA_TIME_SLICE);
  492. X
  493. X    StartCIATimer();
  494. X    return(TRUE);
  495. X}
  496. X
  497. Xmain()
  498. X{
  499. X    if (BeginCIATimer())
  500. X    {
  501. X        Wait(SIGBREAKF_CTRL_C);
  502. X    }
  503. X    else 
  504. X        exit(1);
  505. X
  506. X    EndCIATimer();
  507. X    exit(0);
  508. X}
  509. SHAR_EOF
  510. echo "extracting ciatimer.h"
  511. sed 's/^X//' << \SHAR_EOF > ciatimer.h
  512. X/* TIMER - Amiga CIA Timer Control Software
  513. X
  514. X  originally by Paul Higginbottom, Public Domain
  515. X
  516. X  hacked on by karl to produce a monotonically increasing microsecond
  517. X  clock, 12/30/88, Public Domain
  518. X
  519. X  second release, 4/27/89, Public Domain
  520. X
  521. X  cc +p ciatimer.c
  522. X  ln ciatimer.o -lcl32
  523. X
  524. X  To start the timer, execute BeginCIATimer()
  525. X
  526. X*/
  527. X
  528. Xstruct CIA_Time
  529. X{
  530. X    long CIA_Seconds;
  531. X    long CIA_Microseconds;
  532. X};
  533. X
  534. X/* timeslice is 40000 intervals.  Each interval is 1.396825 microseconds,
  535. X * this should correspond to a timing interval of 55873 microseconds */
  536. X#define CIA_TIME_SLICE ((unsigned short) 40000)
  537. X#define CIA_TIME_QUANTITY ((unsigned short) 55873)
  538. X
  539. X#define CIATIMER_INTERRUPT_NAME "CIA Periodic Timer"
  540. X
  541. SHAR_EOF
  542. echo "extracting makefile"
  543. sed 's/^X//' << \SHAR_EOF > makefile
  544. X
  545. XCFLAGS= +p
  546. X
  547. Xall:    ciatimer ciafinder
  548. X
  549. Xciatimer:    ciatimer.o
  550. X    ln ciatimer.o -lcl32
  551. X
  552. Xciafinder:    ciafinder.o
  553. X    ln ciafinder.o -lcl32
  554. X
  555. Xshar:
  556. X    shar >ciatimer.shar README ciatimer.h ciatimer.c ciafinder.c makefile
  557. SHAR_EOF
  558. echo "End of archive 1 (of 1)"
  559. # if you want to concatenate archives, remove anything after this line
  560. exit
  561.